#include <stdlib.h>
#include <stdio.h>

#include "TDA_ListaOrdenada.h"

/* =============================================================================
 * Declaracion Atributos privados
 * =============================================================================
 */
#define LISTA_FIN NULL

#define TRUE 1
#define FALSE 0

struct Nodo {
  tDato dato;
  struct Nodo* sig;
};

typedef struct Nodo* tNodo;

/*==============================================================================
 * Declaracion de metodos privados
 *==============================================================================
 */

static tNodo nodoCrear (tNodo* pNodo, tDato pDato);

static void nodoDestruir (tNodo pNodo);

static tDato nodoGetDato (tNodo pNodo);

static void nodoSetDato (tNodo pNodo, tDato pDato);

//==============================================================================

struct ListaOrdenada {
    
  tNodo raiz;
  tFcnDestructorDato fcnDestructorDato;
  tFcnListaOrdenadaOrdenPrecedenciaDato fcnOrdenPrecedencia;
};

typedef struct ListaOrdenada* _tListaOrdenada;

static tNodo listaOrdenadaGetRaiz(tListaOrdenada pLista);

/*==============================================================================
 * Definicion de metodos publicos
 *==============================================================================
 */

void listaOrdenadaCrear (tListaOrdenada* pListaOrdenada
                       , tFcnDestructorDato pDestructorDato
                       , tFcnListaOrdenadaOrdenPrecedenciaDato pOrdenPrecedencia)
{
    _tListaOrdenada lista = NULL;
    
    if (pListaOrdenada && pOrdenPrecedencia)
    {
        if (NULL == pDestructorDato) pDestructorDato = & DestructorDato;
        
        lista = (_tListaOrdenada) malloc (sizeof (struct ListaOrdenada));
        if (lista)
        {
            lista->raiz = LISTA_FIN;
            lista->fcnDestructorDato = pDestructorDato;
            lista->fcnOrdenPrecedencia = pOrdenPrecedencia;
        }  
    }
    *pListaOrdenada = lista;
}

//------------------------------------------------------------------------------

void listaOrdenadaDestruir (tListaOrdenada pLista){

    if (pLista)
    {
        while (!listaOrdenadaVacia(pLista))
        {
            tNodo temp = listaOrdenadaGetRaiz(pLista);
            ((_tListaOrdenada)pLista)->raiz = temp->sig;
            ((_tListaOrdenada)pLista)->fcnDestructorDato(nodoGetDato(temp));

            nodoDestruir(temp);
        }
        free ((_tListaOrdenada)pLista);
    }
}

//------------------------------------------------------------------------------

tDato listaOrdenadaInsertar (tListaOrdenada pLista, tDato pDato){
    
    tDato val = DATO_ERROR;
    
    if (pLista && pDato)
    {
        tNodo actual;
        nodoCrear(&actual, pDato);
        
        if (!listaOrdenadaVacia(pLista))
        {
            tNodo previo = NULL
                , cursor = listaOrdenadaGetRaiz(pLista);

            while (LISTA_FIN != cursor)
            {
                if (1 == ((_tListaOrdenada)pLista)->fcnOrdenPrecedencia (pDato, nodoGetDato(cursor)))
                {
                    previo = cursor;
                    cursor = cursor->sig;
                }
                else
                    break;
            }

            if (previo != NULL)
                previo->sig = actual;
            else
                ((_tListaOrdenada)pLista)->raiz = actual;
            
            actual->sig = cursor;
            val = nodoGetDato(pDato);
        }
        else
            ((_tListaOrdenada)pLista)->raiz = actual;
    }
    return val;
}

//------------------------------------------------------------------------------

void listaOrdenadaRemover (tListaOrdenada pLista, tDato pDato) {

    if (!listaOrdenadaVacia(pLista))
    {
        tNodo cursor, buscado, previo;
        
        cursor = ((_tListaOrdenada)pLista)->raiz;
        buscado = NULL;
        previo = NULL;
        while (NULL != cursor && NULL == buscado)
        {
            tFcnListaOrdenadaOrdenPrecedenciaDato fcn = ((_tListaOrdenada)pLista)->fcnOrdenPrecedencia;
            if (0 == fcn (cursor->dato, pDato))
                buscado = cursor;
            else
            {
                previo = cursor;
                cursor = cursor->sig;
            }
        }
        if (NULL != buscado)
        {
            if (NULL == previo)
                ((_tListaOrdenada)pLista)->raiz = buscado->sig;
            else
                previo->sig = buscado->sig;
            
            nodoDestruir(buscado);
        }
    }
}

//------------------------------------------------------------------------------

tBool listaOrdenadaVacia (tListaOrdenada pLista){

  int vacia = TRUE;

  if (pLista)
    vacia =  LISTA_FIN ==((_tListaOrdenada)pLista)->raiz;

  return vacia;
  
}

//------------------------------------------------------------------------------

struct ListaOrdenadaIterador {
  tListaOrdenada lista;
  tNodo cursor;
  tBool enInicio;
};

typedef struct ListaOrdenadaIterador* _tListaOrdenadaIterador;


tListaOrdenadaIterador listaOrdenadaIterador (tListaOrdenada pLista){

  _tListaOrdenadaIterador iterador = NULL;

  if (pLista)
  {
    iterador = (_tListaOrdenadaIterador) malloc (sizeof (struct ListaOrdenadaIterador));
    
    if (iterador)
    {
      iterador->lista = pLista;
      iterador->cursor = NULL;
      iterador->enInicio = TRUE;
    }
  } 
  return iterador;
}

//------------------------------------------------------------------------------

void listaOrdenadaIteradorLiberar (tListaOrdenadaIterador pIterador){

  if (pIterador)
  {
    ((_tListaOrdenadaIterador)pIterador)->lista = NULL;
    ((_tListaOrdenadaIterador)pIterador)->cursor = NULL;
    free ((_tListaOrdenadaIterador)pIterador);
  }
}

//------------------------------------------------------------------------------

tBool listaOrdenadaIteradorMoverPrimero (tListaOrdenadaIterador pIterador){
    
    tBool val = FALSE;
    
    if (pIterador)
    {
        ((_tListaOrdenadaIterador)pIterador)->enInicio = TRUE;
        ((_tListaOrdenadaIterador)pIterador)->cursor = NULL;
        val = TRUE;
    }
    return val;
}

//------------------------------------------------------------------------------

tBool listaOrdenadaIteradorMoverSiguiente (tListaOrdenadaIterador pIterador){
  
  int exito = FALSE;
  _tListaOrdenadaIterador iterador = (_tListaOrdenadaIterador)pIterador;
  
  if (pIterador)  
    if (!listaOrdenadaVacia (iterador->lista))
    {
      if (TRUE == iterador->enInicio)
      {
        iterador->cursor = listaOrdenadaGetRaiz(iterador->lista);
        iterador->enInicio = FALSE;
      }
      else if (LISTA_FIN != iterador->cursor)
        iterador->cursor = iterador->cursor->sig;
        
      exito = iterador->cursor != LISTA_FIN;

    }
  return exito;
}


//------------------------------------------------------------------------------

tDato listaOrdenadaIteradorActual(tListaOrdenadaIterador pIterador){
  
  tDato val = NULL;
  _tListaOrdenadaIterador iterador = (_tListaOrdenadaIterador)pIterador;
  if (pIterador)
  {
    if (LISTA_FIN != iterador->cursor)
      val = nodoGetDato(iterador->cursor);    
  }  
  return val;
}

//==============================================================================

tNodo nodoCrear (tNodo* pNodo, tDato pDato){
    
    tNodo nodo = NULL;
    
    if (pNodo && pDato)
    {
        nodo = (tNodo) malloc (sizeof (struct Nodo));
        if (nodo)
        {
            nodoSetDato(nodo, pDato);
            nodo->sig = LISTA_FIN;
        }
    }
    *pNodo = nodo;
    return nodo;
}

//------------------------------------------------------------------------------

void nodoDestruir (tNodo pNodo){
    
    if (pNodo)
        free(pNodo);
}

//------------------------------------------------------------------------------

tDato nodoGetDato (tNodo pNodo){
    
    tDato dato = NULL;
    
    if (pNodo)
        dato = pNodo->dato;
    
    return dato;
}

//------------------------------------------------------------------------------

void nodoSetDato (tNodo pNodo, tDato pDato){

    if (pNodo && pDato)
        pNodo->dato = pDato;
}


////////////////////////////////////////////////////////////////////////////////
//============================================================================//
////////////////////////////////////////////////////////////////////////////////

tNodo listaOrdenadaGetRaiz(tListaOrdenada pLista){
    
    tNodo nodo = NULL;
    
    if (pLista)
        nodo = ((_tListaOrdenada)pLista)->raiz;
    
    return nodo;
}


